Track type inheritance chain assemblies in symbol-based mode#145
Merged
Track type inheritance chain assemblies in symbol-based mode#145
Conversation
In the symbol-based analysis path, only the immediate base type's containing assembly was credited via `TrackType(namedType.BaseType)`. The C# compiler validates the entire base-type chain plus implemented interfaces at type-check time -- CS0012 fires when *any* link's defining assembly is missing -- so any assembly along the chain must remain a reference. With `<DisableTransitiveProjectReferences>true</...>`, the chain doesn't flow transitively, so the consumer must explicitly reference grandparent assemblies. Symbol-based RT, when handling `INamedTypeSymbol`, walked only the immediate `BaseType` and direct `Interfaces`, never recursing further up. So for `Consumer : Provider` where `Provider : ProviderDependency` in another assembly, the consumer's reference to `ProviderDependency` was wrongly flagged RT0002 removable. Fix: extend `TrackType`'s `INamedTypeSymbol` branch to walk the full `BaseType` chain and `AllInterfaces` collection. `AllInterfaces` is broader than `Interfaces` (includes transitively-implemented interfaces) and mirrors what the compiler validates. A `ConcurrentDictionary<ISymbol, byte>` keyed via `SymbolEqualityComparer.Default` gates the chain walk to break self-referential cycles such as `int -> AllInterfaces[IComparable<int>] -> typeArg int -> ...` and to avoid redundant work. Adds seven regression tests that all fail without this change: - `UsedViaInheritedBaseType`: the canonical issue #144 scenario - `UsedViaImplementedInterface`: interface in the chain - `UsedViaMultiLevelInheritanceChain`: three-level A <- B <- C - `UsedViaInheritanceChainOnVariableType`: chain via parameter type - `UsedViaMixedBaseAndInterfaceChain`: base + interface, four asms - `UsedViaGenericConstraintBaseChain`: `where T : Provider` constraint - `UnrelatedReferenceNotMarkedByInheritance`: negative test ensuring the fix doesn't over-credit unrelated assemblies Verified against upstream repro https://github.com/olstakh/RT_Gap_Inheritance: `Consumer` builds with 0 RT warnings (was emitting RT0002 with v3.5.2). Different code path from #141 (qualifier tracking on inherited static member access), #142 (delegate parameter / return types), and #143 (override chains). Same family of "symbol traversal misses a chain" gaps; same reporter (olstakh). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #144.
In the symbol-based analysis path, only the immediate base type's containing assembly was credited via
TrackType(namedType.BaseType). The C# compiler validates the entire base-type chain plus implemented interfaces at type-check time -- CS0012 fires when any link's defining assembly is missing -- so any assembly along the chain must remain a reference. With<DisableTransitiveProjectReferences>true</...>, the chain doesn't flow transitively, so the consumer must explicitly reference grandparent assemblies.Symbol-based RT, when handling
INamedTypeSymbol, walked only the immediateBaseTypeand directInterfaces, never recursing further up. So forConsumer : ProviderwhereProvider : ProviderDependencyin another assembly, the consumer's reference toProviderDependencywas wrongly flagged RT0002 removable.Fix: extend
TrackType'sINamedTypeSymbolbranch to walk the fullBaseTypechain andAllInterfacescollection.AllInterfacesis broader thanInterfaces(includes transitively-implemented interfaces) and mirrors what the compiler validates. AConcurrentDictionary<ISymbol, byte>keyed viaSymbolEqualityComparer.Defaultgates the chain walk to break self-referential cycles such asint -> AllInterfaces[IComparable<int>] -> typeArg int -> ...and to avoid redundant work.Adds seven regression tests that all fail without this change:
UsedViaInheritedBaseType: the canonical issue [ReferenceTrimmerUseSymbolAnalysis] Account for type inheritance #144 scenarioUsedViaImplementedInterface: interface in the chainUsedViaMultiLevelInheritanceChain: three-level A <- B <- CUsedViaInheritanceChainOnVariableType: chain via parameter typeUsedViaMixedBaseAndInterfaceChain: base + interface, four asmsUsedViaGenericConstraintBaseChain:where T : ProviderconstraintUnrelatedReferenceNotMarkedByInheritance: negative test ensuring the fix doesn't over-credit unrelated assembliesVerified against upstream repro https://github.com/olstakh/RT_Gap_Inheritance:
Consumerbuilds with 0 RT warnings (was emitting RT0002 with v3.5.2).Different code path from #141 (qualifier tracking on inherited static member access), #142 (delegate parameter / return types), and #143 (override chains). Same family of "symbol traversal misses a chain" gaps; same reporter (olstakh).